{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "# Python 系统管理\n",
    "\n",
    "## 文件系统\n",
    "\n",
    "Python的`os`模块实现了操作文件系统的接口。这些操作包括遍历目录树，删除/重命名文件等。此外`os.path`模块可以实现一些针对路径名的操作。\n",
    "\n",
    "\n",
    "### `os`模块的函数\n",
    "\n",
    "#### 文件处理\n",
    "\n",
    "* `remove()` / `unlink()`    删除文件\n",
    "* `rename()` / `renames()`   重命名文件\n",
    "* `stat()`               返回文件信息\n",
    "* `symlink()`            创建符号链接\n",
    "* `utime()`              更新时间戳\n",
    "* `walk()`               生成一个目录树下的所有文件名\n",
    "\n",
    "#### 目录/文件夹\n",
    "\n",
    "* `mkdir()` / `mkdirs()`     创建目录/创建多层目录\n",
    "* `rmdir()` / `removedirs()` 删除目录/删除多层目录\n",
    "* `listdir()`            列出指定目录的文件\n",
    "* `chdir()` / `fcdir()`      改变当前工作目录 / 通过一个文件描述符改变当前目录\n",
    "* `chroot()`             改变当前进程的根目录\n",
    "* `getcwd()` / `getcwdu()`   返回当前工作目录 / 功能相同，但返回Unicode对象\n",
    "\n",
    "#### 访问/权限\n",
    "\n",
    "* `access()`             检验权限模式\n",
    "* `chmod()`              改变权限模式\n",
    "* `chown()` / `lchown()`     改变owner和group ID / 功能相同，但不会跟踪链接\n",
    "* `umask()`              设置默认权限模式\n",
    "\n",
    "\n",
    "### `os.path`模块中的路径名访问函数\n",
    "\n",
    "#### 分隔\n",
    "\n",
    "* `basename()`           去掉目录路径，返回文件名\n",
    "* `dirname()`            去掉文件名，返回目录路径\n",
    "* `join()`               将分隔的部分组合成路径\n",
    "* `split()`              返回`(dirname(), basename())`元组\n",
    "* `splitdrive()`         返回`(drivename, pathname)`元组\n",
    "* `splitext()`           返回`(filename, extension)`元组\n",
    "\n",
    "#### 信息\n",
    "\n",
    "* `getatime()`           返回最近访问时间\n",
    "* `getctime()`           返回文件创建时间\n",
    "* `getmtime()`           返回最近文件修改时间\n",
    "* `getsize()`            返回文件大小\n",
    "\n",
    "#### 查询\n",
    "\n",
    "* `exists()`             指定路径（文件或者目录）是否存在\n",
    "* `isabs()`              指定路径是否为绝对路径\n",
    "* `isdir()`              指定路径是否存在且是一个目录\n",
    "* `isfile()`             指定路径是否存在且是一个文件\n",
    "* `islink()`             指定路径是否存在且是一个符号链接\n",
    "* `ismount()`            指定路径是否存在且是一个挂载点\n",
    "* `samefile()`           两个路径名是否指向同一个文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import os\n",
    "os.path.exists('12_system_management.ipynb')  # True\n",
    "os.path.isfile('12_system_management.ipynb')  # True\n",
    "os.path.isdir('12_system_management.ipynb')  # False\n",
    "os.path.isabs('12_system_management.ipynb')  # False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "list(os.walk('.'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "### 使用`glob`模块列出匹配文件\n",
    "\n",
    "`glob.glob()`函数会使用Unix shell的规则来匹配文件或者目录：\n",
    "\n",
    "* `*` 匹配任意名称（`re`中是`.*`）\n",
    "* `?` 匹配一个字符\n",
    "* `[abc]` 匹配字符`a`、`b`和`c`\n",
    "* `[!abc]` 匹配出了`a`、`b`和`c`之外所有字符"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import glob\n",
    "\n",
    "glob.glob('*.ipynb')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "## 日期和时间\n",
    "\n",
    "### `datetime`模块\n",
    "\n",
    "其定义了4个主要的对象，每个对象处理的内容：\n",
    "\n",
    "* `date` 处理年、月、日\n",
    "* `time` 处理时、分、秒和微秒\n",
    "* `datetime` 处理日期和时间同时出现的情况\n",
    "* `timedelta` 处理日期和（或）时间间隔\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "from datetime import date\n",
    "\n",
    "\n",
    "halloween = date(2017, 4, 21)\n",
    "halloween\n",
    "print(halloween.day, halloween.month, halloween.year)\n",
    "halloween.isoformat()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "**iso**是指ISO 8601，一种表示日期和时间的国际标准。这个标准的显示顺序是从一般（年）到特殊（日）。其可用来对日期进行正确的排序：先按照年，然后是月，最后是日。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "now = date.today()\n",
    "now"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "from datetime import timedelta\n",
    "\n",
    "one_day = timedelta(days=1)\n",
    "tomorrow = now + one_day\n",
    "print(tomorrow)\n",
    "print(now + 17 * one_day)\n",
    "yesterday = now - one_day\n",
    "print(yesterday)\n",
    "\n",
    "from datetime import datetime\n",
    "print(repr(datetime.resolution))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "date的范围是`date.min`到`date.max`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(date.min)\n",
    "print(date.max)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "`datetime`模块中的`time`对象用来表示一天中的时间："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "from datetime import time\n",
    "\n",
    "noon = time(12, 0, 0)\n",
    "print(noon)\n",
    "print(noon.hour, noon.minute, noon.second, sep=':')\n",
    "print(noon.microsecond)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "参数的顺序按照时间单位从大到小排列（时、分、秒、微秒）。没有参数的话，`time`会默认使用0。\n",
    "\n",
    "注意，时间不一定时精确的，对于**微秒**和**秒**。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "from datetime import datetime\n",
    "\n",
    "def print_repr(obj):\n",
    "    print(repr(obj))\n",
    "\n",
    "some_day = datetime(2017, 4, 21, 2, 43, 50, 7)\n",
    "print_repr(some_day.isoformat())\n",
    "\n",
    "right_now = datetime.now()\n",
    "print_repr(right_now)\n",
    "\n",
    "from datetime import time, date\n",
    "noon = time(12)\n",
    "this_day = date.today()\n",
    "noon_today = datetime.combine(this_day, noon)\n",
    "print_repr(noon_today)\n",
    "\n",
    "print_repr(noon_today.date())\n",
    "print_repr(noon_today.time())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "下面的代码展示计算一个月份的开始日到结束日中间的日期范围："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "from datetime import datetime, date, timedelta\n",
    "import calendar\n",
    "\n",
    "\n",
    "def get_month_range(start_date=None):\n",
    "    if start_date is None:\n",
    "        start_date = date.today().replace(day=1)\n",
    "    _, days_in_month = calendar.monthrange(start_date.year, start_date.month)\n",
    "    end_date = start_date + timedelta(days=days_in_month)\n",
    "    return (start_date, end_date)\n",
    "\n",
    "\n",
    "a_day = timedelta(days=1)\n",
    "first_day, last_day = get_month_range()\n",
    "while first_day < last_day:\n",
    "    print(first_day)\n",
    "    first_day += a_day\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "上面的`get_month_range()`函数接受一个`datetime`对象并返回一个由当前月份开始日和下个月开始日组成的元组对象。\n",
    "\n",
    "计算出一个对应月份第一天的日期，一种快速的方法就是使用`date`或`datetime`对象的`replace()`方法简单地将`days`属性设置成`1`即可。\n",
    "\n",
    "使用`calendar.monthrange()`来获得该月的总天数。任何时候只要你想获得日历信息，可以使用`calendar`模块。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "### `time`模块\n",
    "\n",
    "一种表示绝对时间的方法时计算从某个起始点开始的秒数。Unix时间使用从1970年1月1日0点开始的秒数。这个值通常被成为**纪元**（epoch），它是不同系统之间最简单的交换日期时间的方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "# time() 返回当前时间的纪元值\n",
    "now = time.time()\n",
    "print_repr(now)\n",
    "\n",
    "# ctime() 将纪元值转换成一个字符串\n",
    "print_repr(time.ctime(now))\n",
    "\n",
    "# localtime() 返回当前系统时区下的时间\n",
    "print_repr(time.localtime(now))\n",
    "\n",
    "# gmtime() 返回UTC时间\n",
    "print_repr(time.gmtime(now))\n",
    "\n",
    "print_repr(time.localtime())\n",
    "print_repr(time.gmtime())\n",
    "\n",
    "# mktime() 将 struct_time 对象转换回纪元值\n",
    "print_repr(time.mktime(time.localtime()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "`localtime()`和`gmttime()`返回的是一个`struct_time`对象（命名元组）。其结构如下：\n",
    "\n",
    "| Index | Attribute | Values                                           |\n",
    "|-------|-----------|--------------------------------------------------|\n",
    "|     0 | tm_year   | (for example, 1993)                              |\n",
    "|     1 | tm_mon    | range [1, 12]                                    |\n",
    "|     2 | tm_mday   | range [1, 31]                                    |\n",
    "|     3 | tm_hour   | range [0, 23]                                    |\n",
    "|     4 | tm_min    | range [0, 59]                                    |\n",
    "|     5 | tm_sec    | range [0, 61];                                   |\n",
    "|     6 | tm_wday   | range [0, 6], Monday is 0                        |\n",
    "|     7 | tm_yday   | range [1, 366]                                   |\n",
    "|     8 | tm_isdst  | 0, 1 or -1;                                      |\n",
    "|   N/A | tm_zone   | abbreviation of timezone name                    |\n",
    "|   N/A | tm_gmtoff | offset east of UTC in seconds                    |\n",
    "\n",
    "\n",
    "**建议：**\n",
    "\n",
    "* 尽量多使用UTC来代替时区，特别是将服务器设置为UTC时间，不要使用本地时间。\n",
    "* 有可能的话绝对不使用夏时制时间。\n",
    "\n",
    "\n",
    "### 读写日期和时间\n",
    "\n",
    "使用`strftime()`将日期和时间转换成字符串，`datetime`、`date`、`time`对象和`time`模块中都包含此方法。`strftime()`使用格式化字符串来指定输出，见下表：\n",
    "\n",
    "| 格式化字符串 | 日期/时间单元  |        范围 |\n",
    "|--------------|----------------|-------------|\n",
    "| Y            | 年             |    1900-... |\n",
    "| m            | 月             |       01-12 |\n",
    "| B            | 月名           | January,... |\n",
    "| b            | 月名简写       |     Jan,... |\n",
    "| d            | 日             |       01-31 |\n",
    "| A            | 星期           |  Sunday,... |\n",
    "| a            | 星期缩写       |     Sun,... |\n",
    "| H            | 时（24小时制） |       00-23 |\n",
    "| I            | 时（12小时制） |       01-12 |\n",
    "| p            | 上午/下午      |       AM,PM |\n",
    "| M            | 分             |       00-59 |\n",
    "| S            | 秒             |       00-59 |\n",
    "\n",
    "数字左侧都是补零。更多内容请参考[官方文档](https://docs.python.org/3.6/library/datetime.html#strftime-strptime-behavior)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "fmt = \"It's %A, %B %d, %Y, local time %I:%M:%S%p\"\n",
    "t = time.localtime()\n",
    "print_repr(t)\n",
    "print(time.strftime(fmt, t))\n",
    "\n",
    "\n",
    "from datetime import date\n",
    "\n",
    "some_day = date(2017, 4, 21)\n",
    "print(some_day.strftime(fmt))  # 只能获取日期部分，时间默认是午夜\n",
    "\n",
    "\n",
    "from datetime import time\n",
    "\n",
    "some_time = time(10, 35)\n",
    "print(some_time.strftime(fmt))  # 只会转换时间部分"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "使用`strptime()`可以将格式化的字符串转换为日期或时间。不能使用正则表达式，字符串的非格式化部分必须完全匹配。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import time\n",
    "\n",
    "fmt = '%Y-%m-%d'\n",
    "print_repr(time.strptime('2017-04-21', fmt))\n",
    "print_repr(time.strptime('2017-04-31', fmt))  # ValueError"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "名称可以通过操作系统中的`locale`进行设置。如果要打印不同的月和日名称，可通过`setlocale()`来设置，其第一个参数是`locale.LC_TIME`，表示设置的是日期和时间，第二个参数是一个结合了**语言**和**国家名称**的缩写字符串。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import locale\n",
    "help(locale.setlocale)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import locale\n",
    "from datetime import date\n",
    "\n",
    "halloween = date(2014, 10, 31)\n",
    "for lang_country in ['en_us', 'fr_fr', 'de_de', 'zh_cn']:\n",
    "    locale.setlocale(locale.LC_TIME, lang_country)\n",
    "    print(halloween.strftime('%A, %B %d'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "autoscroll": false,
    "collapsed": false,
    "ein.hycell": false,
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import locale\n",
    "names = locale.locale_alias.keys()\n",
    "good_names = [name for name in names\n",
    "              if len(name) == 5 and name[2] == '_']\n",
    "for name in list(good_names)[-5:]:\n",
    "    print(name)\n",
    "\n",
    "zh = [name for name in good_names if name.startswith('zh')]\n",
    "print_repr(zh)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "ein.tags": "worksheet-0",
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "#### 其他操作日期和时间的类库\n",
    "\n",
    "* [arrow](https://github.com/crsmithdev/arrow)：更好的 Python 日期时间操作类库。\n",
    "* [maya](https://github.com/kennethreitz/maya)：Timestamps for humans.\n",
    "* [Chronyk](https://github.com/KoffeinFlummi/Chronyk)：Python 3 的类库，用于解析手写格式的时间和日期。\n",
    "* [dateutil](https://pypi.python.org/pypi/python-dateutil)：Python datetime 模块的扩展。\n",
    "* [delorean](https://github.com/myusuf3/delorean/)：解决 Python 中有关日期处理的棘手问题的库。\n",
    "* [moment](https://github.com/zachwill/moment)：一个用来处理时间和日期的Python库。灵感来自于Moment.js。\n",
    "* [PyTime](https://github.com/shinux/PyTime)：一个简单易用的Python模块，用于通过字符串来操作日期/时间。\n",
    "* [pytz](https://launchpad.net/pytz)：现代以及历史版本的世界时区定义。将时区数据库引入Python。\n",
    "* [when.py](https://github.com/dirn/When.py)：提供用户友好的函数来帮助用户进行常用的日期和时间操作。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "name": "12_system_management.ipynb"
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
